#include "music.h"
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_heap_caps.h"
#include "esp_spiffs.h"
#include "driver/i2s.h"
#include "esp_log.h"
#include "es8311.h"
#include "mp3dec.h"
#include "board.h"
#include "driver/gpio.h"
#include "esp_spiffs.h"
#include "i2c_bus.h"
static const char *TAG = "AUDIO";

#define SAMPLE_RATE     (44100)
#define I2S_NUM         (0)
#define WAVE_FREQ_HZ    (100)
#define PI              (3.14159265)

#define SAMPLE_PER_CYCLE (SAMPLE_RATE/WAVE_FREQ_HZ)

#define AUDIO_MAX_PLAY_LIST 2

/*!< aduio music list from spiffs*/
const char audio_list[AUDIO_MAX_PLAY_LIST][64] =
{
    // "/spiffs/To_meet_the_prime_time_44k.mp3",
    "/spiffs/myheart_44k.mp3",
    "/spiffs/lemon_tree_8k.mp3"
};

enum
{
    AUDIO_STOP = 0,
    AUDIO_PLAY,
    AUDIO_NEXT,
    AUDIO_LAST
};

unsigned char isPlay = 0;
int play_flag = AUDIO_STOP;
int audio_play_index = 0;

void aplay_mp3(const char *path)
{
    ESP_LOGI(TAG, "start to decode %s", path);
    HMP3Decoder hMP3Decoder;
    MP3FrameInfo mp3FrameInfo;
    unsigned char *readBuf = malloc(MAINBUF_SIZE);

    if (readBuf == NULL)
    {
        ESP_LOGE(TAG, "readBuf malloc failed");
        return;
    }

    short *output = malloc(1153 * 4);

    if (output == NULL)
    {
        free(readBuf);
        ESP_LOGE(TAG, "outBuf malloc failed");
    }

    hMP3Decoder = MP3InitDecoder();

    if (hMP3Decoder == 0)
    {
        free(readBuf);
        free(output);
        ESP_LOGE(TAG, "memory is not enough..");
    }

    int samplerate = 0;
    i2s_zero_dma_buffer(0);
    FILE *mp3File = fopen(path, "rb");

    if (mp3File == NULL)
    {
        MP3FreeDecoder(hMP3Decoder);
        free(readBuf);
        free(output);
        ESP_LOGE(TAG, "open file failed");
    }

    char tag[10];
    int tag_len = 0;
    int read_bytes = fread(tag, 1, 10, mp3File);

    if (read_bytes == 10)
    {
        if (memcmp(tag, "ID3", 3) == 0)
        {
            tag_len = ((tag[6] & 0x7F) << 21) | ((tag[7] & 0x7F) << 14) | ((tag[8] & 0x7F) << 7) | (tag[9] & 0x7F);
            // ESP_LOGI(TAG,"tag_len: %d %x %x %x %x", tag_len,tag[6],tag[7],tag[8],tag[9]);
            fseek(mp3File, tag_len - 10, SEEK_SET);
        }
        else
        {
            fseek(mp3File, 0, SEEK_SET);
        }
    }

    int bytesLeft = 0;
    unsigned char *readPtr = readBuf;


    while (1)
    {

        switch (play_flag)
        {
            case AUDIO_STOP:
            {
                isPlay = 0;
                while (!play_flag)
                {
                    i2s_zero_dma_buffer(0);
                    vTaskDelay(100 / portTICK_RATE_MS);
                }
            }
            break;

            case AUDIO_PLAY:
            {
                isPlay = 1;
            }
            break;

            case AUDIO_NEXT:
            {

                if (isPlay)
                {
                    play_flag = AUDIO_PLAY;
                }
                else
                {
                    play_flag = AUDIO_STOP;
                }

                goto stop;
            }
            break;

            case AUDIO_LAST:
            {

                if (isPlay)
                {
                    play_flag = AUDIO_PLAY;
                }
                else
                {
                    play_flag = AUDIO_STOP;
                }
                goto stop;
            }
            break;
        }

        if (bytesLeft < MAINBUF_SIZE)
        {
            memmove(readBuf, readPtr, bytesLeft);
            int br = fread(readBuf + bytesLeft, 1, MAINBUF_SIZE - bytesLeft, mp3File);

            if ((br == 0) && (bytesLeft == 0))
            {
                break;
            }

            bytesLeft = bytesLeft + br;
            readPtr = readBuf;
        }

        int offset = MP3FindSyncWord(readPtr, bytesLeft);

        if (offset < 0)
        {
            ESP_LOGE(TAG, "MP3FindSyncWord not find");
            bytesLeft = 0;
            continue;
        }
        else
        {
            readPtr += offset;                    /*!< data start point */
            bytesLeft -= offset;                 /*!< in buffer */
            int errs = MP3Decode(hMP3Decoder, &readPtr, &bytesLeft, output, 0);

            if (errs != 0)
            {
                ESP_LOGE(TAG, "MP3Decode failed ,code is %d ", errs);
                break;
            }

            MP3GetLastFrameInfo(hMP3Decoder, &mp3FrameInfo);

            if (samplerate != mp3FrameInfo.samprate)
            {
                samplerate = mp3FrameInfo.samprate;
                i2s_set_clk(0, samplerate, 16, mp3FrameInfo.nChans);
                ESP_LOGI(TAG, "mp3file info---bitrate=%d,layer=%d,nChans=%d,samprate=%d,outputSamps=%d", mp3FrameInfo.bitrate, mp3FrameInfo.layer, mp3FrameInfo.nChans, mp3FrameInfo.samprate, mp3FrameInfo.outputSamps);
            }

            size_t bytes_write = 0;
            i2s_write(0, (const char *) output, mp3FrameInfo.outputSamps * 2, &bytes_write, 200 / portTICK_RATE_MS);
            // rmt_write_items(0,(const char*)output,mp3FrameInfo.outputSamps*2, 1000 / portTICK_RATE_MS);
        }
    }

stop:
    i2s_zero_dma_buffer(0);
    MP3FreeDecoder(hMP3Decoder);
    free(readBuf);
    free(output);
    fclose(mp3File);

    ESP_LOGI(TAG, "end mp3 decode ..");
}

static void audio_task(void *arg)
{
    /*!<  for 36Khz sample rates, we create 100Hz sine wave, every cycle need 36000/100 = 360 samples (4-bytes or 8-bytes each sample) */
    /*!<  depend on bits_per_sample */
    /*!<  using 6 buffers, we need 60-samples per buffer */
    /*!<  if 2-channels, 16-bit each channel, total buffer is 360*4 = 1440 bytes */
    /*!<  if 2-channels, 24/32-bit each channel, total buffer is 360*8 = 2880 bytes */
    i2s_config_t i2s_config =
    {
        .mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX,                                  /*!<  Only TX */
        .sample_rate = SAMPLE_RATE,
        .bits_per_sample = 16,
        .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,                           /*!< 1-channels */
        .communication_format = I2S_COMM_FORMAT_I2S,
        .dma_buf_count = 6,
        .dma_buf_len = 256,
        .use_apll = true,
        .tx_desc_auto_clear = true,     /*!< I2S auto clear tx descriptor if there is underflow condition (helps in avoiding noise in case of data unavailability) */
        .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2 | ESP_INTR_FLAG_IRAM,
    };

    i2s_pin_config_t pin_config =
    {
        .bck_io_num = GPIO_NUM_18,
        .ws_io_num = GPIO_NUM_17,
        .data_out_num = GPIO_NUM_12,
        .data_in_num = GPIO_NUM_34                                          /*!< Not used */
    };

    i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
    i2s_set_pin(I2S_NUM, &pin_config);

    while (1)
    {
        aplay_mp3(audio_list[audio_play_index]);
        vTaskDelay(1000 / portTICK_RATE_MS);
    }
}

void music_play()
{
    play_flag = AUDIO_PLAY;
}
void music_stop()
{
    play_flag = AUDIO_STOP;
}
void music_next()
{
    play_flag = AUDIO_NEXT;
    if (audio_play_index < AUDIO_MAX_PLAY_LIST - 1)
    {
        audio_play_index++;
    }
    else
    {
        audio_play_index = 0;
    }
}
void music_last()
{
    play_flag = AUDIO_LAST;
    if (audio_play_index > 0)
    {
        audio_play_index--;
    }
    else
    {
        audio_play_index = AUDIO_MAX_PLAY_LIST - 1;
    }
}
void music_set_vol(unsigned char vol)
{
    es8311_set_voice_volume(vol);
}
unsigned char music_get_vol()
{
    int x = 0;    
    es8311_get_voice_volume(&x);
    return x;
}
char *music_name_now()
{
    return audio_list[audio_play_index];
}


esp_err_t spiffs_init(void)
{
    esp_err_t ret = ESP_OK;
    ESP_LOGI(TAG, "Initializing SPIFFS");

    esp_vfs_spiffs_conf_t conf =
    {
        .base_path = "/spiffs",
        .partition_label = NULL,
        .max_files = 5,
        .format_if_mount_failed = true
    };

    /*!< Use settings defined above to initialize and mount SPIFFS filesystem. */
    /*!< Note: esp_vfs_spiffs_register is an all-in-one convenience function. */
    ret = esp_vfs_spiffs_register(&conf);

    if (ret != ESP_OK)
    {
        if (ret == ESP_FAIL)
        {
            ESP_LOGE(TAG, "Failed to mount or format filesystem");
        }
        else if (ret == ESP_ERR_NOT_FOUND)
        {
            ESP_LOGE(TAG, "Failed to find SPIFFS partition");
        }
        else
        {
            ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret));
        }

        return ret;
    }

    size_t total = 0, used = 0;
    ret = esp_spiffs_info(NULL, &total, &used);

    if (ret != ESP_OK)
    {
        ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret));
    }
    else
    {
        ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
    }

    /*!< Open renamed file for reading */
    ESP_LOGI(TAG, "Reading file");
    FILE *f = fopen("/spiffs/spiffs.txt", "r");

    if (f == NULL)
    {
        ESP_LOGE(TAG, "Failed to open file for reading");
        return ESP_FAIL;
    }

    char line[64];
    fgets(line, sizeof(line), f);
    fclose(f);
    /*!< strip newline */
    char *pos = strchr(line, '\n');

    if (pos)
    {
        *pos = '\0';
    }

    ESP_LOGI(TAG, "Read from file: '%s'", line);

    return ESP_OK;
}

void music_init()
{
    ESP_ERROR_CHECK(spiffs_init());
    ESP_ERROR_CHECK(i2c_bus_init());
    es8311_init(SAMPLE_RATE);
    play_flag = AUDIO_STOP;
    isPlay = 0;
    es8311_set_voice_volume(60);
    xTaskCreate(audio_task, "audio_task", 4096, NULL, 5, NULL);
}
